<template>
  <div class="data-grid-container">
    <el-row>
      <el-col :lg="16" :md="16" :sm="16" :xs="24">
        <el-form :inline="true">
          <el-form-item v-for="item in buttons">
            <!-- 按钮插槽 -->
            <slot :name="item.buttonSlot" v-bind="item"></slot>
          </el-form-item>
        </el-form>
      </el-col>
      <el-col :lg="8" :md="8" :sm="8" :xs="24" class="data-grid-config-button">
        <el-form :inline="true">
          <el-form-item v-if="showRefreshButton">
            <el-tooltip
              class="box-item"
              effect="dark"
              :content="t('common.refreshButton')"
              placement="top"
            >
              <el-icon :size="18" @click="handleGridRefresh">
                <ico-svg icon="grid-refresh-line"></ico-svg>
              </el-icon>
            </el-tooltip>
          </el-form-item>
          <el-form-item v-if="showColumnHeightButton">
            <el-tooltip
              class="box-item"
              effect="dark"
              :content="t('common.gridColumnHeightButton')"
              placement="top"
            >
              <el-dropdown trigger="click" @command="handleGridColumnHeight">
                <el-icon :size="18" class="">
                  <ico-svg icon="grid-line-height"></ico-svg>
                </el-icon>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item command="default">{{
                      t("common.gridColumnHeightDefault")
                    }}</el-dropdown-item>
                    <el-dropdown-item command="large">{{
                      t("common.gridColumnHeightLarge")
                    }}</el-dropdown-item>
                    <el-dropdown-item command="small">{{
                      t("common.gridColumnHeightSmall")
                    }}</el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </el-tooltip>
          </el-form-item>
          <el-form-item v-if="showSettingsButton">
            <el-popover placement="bottom-end" :width="200" trigger="click">
              <template #reference>
                <el-button link>
                  <el-tooltip
                    class="box-item"
                    effect="dark"
                    :content="t('common.gridColumnSettingsButton')"
                    placement="top"
                  >
                    <el-icon :size="18">
                      <ico-svg
                        icon="grid-settings"
                      ></ico-svg></el-icon></el-tooltip
                ></el-button>
              </template>
              <div
                style="
                  border-bottom: 1px solid #ddd;
                  text-align: right;
                  padding-bottom: 10px;
                  padding-right: 10px;
                "
              >
                <el-link type="primary" @click="handleGridResetColumn"
                  >{{ t("common.resetButton") }}
                </el-link>
              </div>
              <el-scrollbar max-height="400px">
                <div v-for="(column, index) in state.columns">
                  <el-checkbox v-if="column.finalTitle" v-model="column.visible"
                    >{{ column.finalTitle }}
                  </el-checkbox>
                </div>
              </el-scrollbar>
            </el-popover>
          </el-form-item>
        </el-form>
      </el-col>
    </el-row>

    <!-- 表格内容，具名插槽 titleSlot 自定义标题，tdSlot 自定义数据区域的每一个列-->
    <!-- Table 默认排序方式 order: ascending / descending -->
    <!-- size	Table 的尺寸	string	large / default /small -->
    <!-- 渲染树形数据时，必须要指定 row-key -->
    <el-table
      stripe
      :row-key="rowKey"
      :show-header="showHeader"
      :lazy="lazy"
      :load="load"
      :tree-props="treeProps"
      :data="data != undefined ? data : state.rows"
      tooltip-effect="dark"
      header-cell-class-name="data-grid-header-cell"
      class="data-grid"
      class-name="test-ellipsis"
      :empty-text="state.emptyText"
      :default-sort="defaultSort"
      :border="border"
      :size="state.size"
      highlight-current-row
      :default-expand-all="defaultExpandAll"
      @sort-change="handleSortChange"
      @current-change="handleRowSelectCurrentChange"
      @selection-change="handleRowSelectionChange"
      @row-dblclick="handleRowDblclick"
    >
      <template v-for="item in state.columns">
        <el-table-column
          v-if="item.visible && !!item.field && state.langChanged"
          v-bind="item"
          :key="item.finalTitle"
          :label="item.finalTitle"
          :prop="item.field"
          :sortable="item.sortable ? 'custom' : false"
          :resizable="item.resizable"
          :align="item.align"
          :header-align="item.headerAlign"
        >
          <template #header="column1" v-if="item.titleSlot">
            <slot
              :item="item"
              :row="column1.row"
              :name="item.titleSlot"
              v-bind="column1"
            ></slot>
          </template>
          <!-- item 是列信息，row 是数据信息，column2 是字段元数据，名字可以随意起名，名字只在当前作用域有效 -->
          <template #default="column2" v-if="item.tdSlot">
            <slot
              :item="item"
              :row="column2.row"
              v-bind="column2"
              :name="item.tdSlot"
            >
            </slot>
          </template>
        </el-table-column>
      </template>
    </el-table>

    <el-row class="data-grid-pagination" v-if="pagination.show">
      <el-col :lg="24" :md="0" :sm="0" :xs="0">
        <el-pagination
          :currentPage="state.currentPage"
          :page-size="state.pageSize"
          :page-sizes="pagination.pageSizes"
          :layout="pagination.layout"
          :total="state.total"
          :small="false"
          :disabled="false"
          :background="true"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          style="text-align: right"
        >
        </el-pagination>
      </el-col>
      <el-col :lg="0" :md="24" :sm="24" :xs="0">
        <el-pagination
          :currentPage="state.currentPage"
          :page-size="state.pageSize"
          :page-sizes="pagination.pageSizes"
          layout="prev, pager, next, jumper"
          :total="state.total"
          :background="true"
          style="text-align: right"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        ></el-pagination>
      </el-col>
      <el-col :lg="0" :md="0" :sm="0" :xs="24">
        <el-pagination
          :currentPage="state.currentPage"
          :page-size="state.pageSize"
          :page-sizes="pagination.pageSizes"
          layout="prev, next, jumper"
          :total="state.total"
          :background="true"
          style="text-align: right"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        ></el-pagination>
      </el-col>
    </el-row>
  </div>
</template>
<script setup lang="ts">
import {
  ref,
  reactive,
  watch,
  onBeforeMount,
  onMounted,
  onUnmounted,
  computed,
} from "vue";
import bus from "../utils/bus";
import router from "../utils/router";
import { useStore } from "../stores/store";
import { useI18n } from "vue-i18n";
const { t } = useI18n({ useScope: "global" });
const store = useStore();

interface Column {
  // 新版本为函数，用于动态切换语言，为了兼容旧版本 any 类型
  title?: any;
  // 经过处理后，最终得到的标题
  finalTitle: string;
  field: string;
  align?: string;
  headerAlign?: string;
  sortable?: boolean;
  width?: number;
  hidden?: boolean;
  visible?: boolean;
  resizable?: boolean;
  filters?: Function;
  wrap?: boolean;
  titleSlot: any;
  labelSlot: any;
  tdSlot: any;
}

// ts 语法，有默认值版本
const props = withDefaults(
  defineProps<{
    // 表格唯一标识，用 key 得不到，用于缓存表格前台配置使用
    id: string;
    // 业务数据主键
    rowKey: string;
    title?: any;
    border?: boolean;
    // 是否显示表头
    showHeader?: boolean;
    // emptyText?: string;
    // Table 默认排序方式 order: ascending / descending
    defaultSort: string;
    // Table 默认排序字段
    defaultSortBy: string;
    defaultExpandAll: boolean;
    // Table 的尺寸	string	large / default /small
    size?: string;
    // 表格操作按钮
    buttons?: {
      buttonSlot: any;
    };
    showRefreshButton?: boolean;
    showColumnHeightButton?: boolean;
    showSettingsButton?: boolean;
    // 请求数据的方法
    request: Function;
    // 表头配置Array
    columns: Column[];

    pagination?: {
      show: boolean;
      // 每页显示条目数的初始值
      pageSize: number;
      // 每页显示个数选择器的选项设置
      pageSizes: Array<number>;
      layout: string;
    };
    // 表格控件回调函数，也可以通过事件接收，推荐使用事件方式
    rowSelectCurrentChange?: Function;
    rowDblclick?: Function;
    rowSelectionChange: Function | undefined;

    // 树形数据与懒加载，一下四个属性必须，如果有懒加载时，优先懒加载
    lazy: boolean;
    data: any;
    load: any;
    treeProps: any;
  }>(),
  {
    title: null,
    border: false,
    showHeader: true,
    // Table 的默认排序方式 order: ascending / descending
    defaultSort: "descending",
    defaultSortBy: "",
    defaultExpandAll: false,
    // emptyText: "暂无数据",
    // Table 的尺寸	string	large / default /small
    size: "default",
    rowKey: "id",
    showRefreshButton: true,
    showColumnHeightButton: true,
    showSettingsButton: true,
    // 只读的属性
    pagination: () => ({
      show: true,
      pageSize: 20,
      pageSizes: [20, 50, 100],
      layout: "total, sizes, prev, pager, next, jumper",
    }),
    lazy: false,
    data: undefined,
    load: undefined,
    treeProps: undefined,
  }
);

const emit = defineEmits<{
  (event: "on-row-current-change", currentRow: any, oldCurrentRow: any): void;
  (event: "on-row-selection-change", selection: any): void;
  (event: "on-row-dblclick", row: any, column: any): void;
}>();

// 要处理的数据，需要从 prop 转为本地数据 state
const state = reactive({
  langChanged: true,
  loading: false,
  // Table 的尺寸	string	large / default /small
  size: props.size,
  emptyText: "暂无数据",

  rows: [],
  total: 0,
  // 当前页数
  currentPage: 1,
  pageSize: props.pagination.pageSize,
  // 排序字段
  sort: props.defaultSortBy,
  // 升序降序 order: ascending / descending
  order: props.defaultSort == "ascending" ? "asc" : "desc",

  columns: [
    {
      title: null,
      finalTitle: "",
      field: "",
      align: "",
      headerAlign: "",
      sortable: false,
      width: 0,
      hidden: false,
      resizable: true,

      filters: Function,
      wrap: false,
      titleSlot: null,
      labelSlot: null,
      tdSlot: null,
    },
  ] as Column[],

  // 行选数据
  currentRow: null,
  // 勾选数据
  multipleSelection: null,
});

onBeforeMount(() => {
  handleBindColumn();
  loadGridData();
});

let unwatch = null;
onMounted(() => {
  // 持久化表格列显示隐藏设置结果 fix el-popover 的 hide 事件不触发，所以只能监控数据
  unwatch = watch(
    () => state.columns,
    (count, prevCount) => {
      if (!props.id) {
        console.error(
          "datagrid props id 必须有值且全局唯一",
          router.currentRoute.value.path
        );
      }
      let columns = Array();
      // 数据在 onBeforeMount 加载
      state.columns.forEach((value, index, array) => {
        // debugger;
        if (!!value.field && !!value.finalTitle) {
          columns.push({
            field: value.field,
            finalTitle: value.finalTitle,
            hidden: !value.visible,
          });
        }
      });
      store.setTableColumns(props.id, columns);
    },
    { deep: true }
  );

  // 语言切换，需要重新绑定列名称
  bus.on("lang-changed", (lang: string) => {
    state.emptyText = t("common.emptyText");
    handleBindColumn();
  });
});

onUnmounted(() => {
  if (unwatch) {
    unwatch();
  }
  // console.log("grid onUnmounted");
  bus.off("lang-changed");
});

// 处理 props 和 store 本地存储的默认值，转换 props 到 state
const handleBindColumn = () => {
  let _columns = store.getColumns(props.id);
  // console.log("handleBindColumn", props.columns[0]);
  state.columns.splice(0);
  props.columns.forEach((value, index, array) => {
    // 不修改原始 props
    let _value = Object.assign({}, value);
    // console.log("handleBindColumn _value", _value);

    if (!_value.field) {
      console.error(
        "datagrid column field not null",
        router.currentRoute.value.path,
        _value
      );
    }
    if (_value.title instanceof Function) {
      _value.finalTitle = _value.title();
    } else {
      _value.finalTitle = _value.title;
    }
    if (!_value.hidden) {
      _value.hidden = false;
    }
    //用本地缓存更新
    _columns.forEach((_column) => {
      if (
        _value.field == _column.field &&
        _value.finalTitle == _column.finalTitle
      ) {
        // debugger;
        _value.hidden = _column.hidden;
      }
    });
    _value.visible = !_value.hidden;
    state.columns.push(_value);
  });
  // console.log("state.columns", state.columns);
};

// 请求列表数据
const loadGridData = async () => {
  if (props.request != undefined) {
    state.emptyText = t("common.emptyText");
    state.loading = true;
    // 数据是具体业务负责请求的，表格提供了分页参数，对应调用端的 params
    const { rows, total } = await props.request({
      // 当前页码
      page: props.pagination.show ? state.currentPage : 1,
      // 每页数量，不分页的情况限制最大请求 9999 条数据
      rows: props.pagination.show ? state.pageSize : 9999,
      // ...searchModel,
      sort: state.sort,
      order: state.order,
    });

    // debugger;
    // console.log(rows, total);
    state.loading = false;
    // 如果有 formatter，需要渲染
    state.rows = rows;
    state.total = total;
  }
};

// 每页显示记录数修改事件
const handleSizeChange = (val: number) => {
  // console.log(`${val} items per page`);
  state.pageSize = val;
  loadGridData();
};
// 翻页事件
const handleCurrentChange = (val: number) => {
  // console.log(`current page: ${val}`);
  state.currentPage = val;
  loadGridData();
};

const handleGridRefresh = () => {
  loadGridData();
};
const handleGridColumnHeight = (command: string) => {
  state.size = command;
};
const handleGridResetColumn = () => {
  state.columns.splice(0);

  let columns = Array();
  props.columns.forEach((value, index, array) => {
    // 不修改原始 props
    let _value = Object.assign({}, value);
    // console.log("handleGridResetColumn _value", _value);

    if (!_value.field) {
      console.error(
        "datagrid column field not null",
        router.currentRoute.value.path,
        _value
      );
    }
    if (_value.title instanceof Function) {
      _value.finalTitle = _value.title();
    } else {
      _value.finalTitle = _value.title;
    }
    if (!_value.hidden) {
      _value.hidden = false;
    }

    _value.visible = !_value.hidden;
    state.columns.push(_value);

    // debugger;
    if (!!_value.field && !!_value.finalTitle) {
      columns.push({
        field: _value.field,
        finalTitle: _value.finalTitle,
        hidden: !_value.visible,
      });
    }
  });
  // console.log("handleGridResetColumn", columns);
  // 重置本地存储
  store.setTableColumns(props.id, columns);
};
const handleSortChange = (sort: any) => {
  // {column, prop, order}
  // console.log("handleSortChange", sort);
  if (sort.column != null) {
    state.sort = sort.prop;
    state.order = sort.order == "ascending" ? "asc" : "desc";
  } else {
    // 默认排序
    // 排序字段
    state.sort = props.defaultSortBy;
    // 升序降序
    state.order = props.defaultSort == "ascending" ? "asc" : "desc";
  }
  loadGridData();
};

//单行选择事件
const handleRowSelectCurrentChange = (
  currentRow: any | undefined,
  oldCurrentRow: any | undefined
) => {
  // console.log(`handleRowSelectCurrentChange `, currentRow);
  state.currentRow = currentRow;
  if (props.rowSelectCurrentChange != undefined) {
    props.rowSelectCurrentChange(currentRow, oldCurrentRow);
  }
  emit("on-row-current-change", currentRow, oldCurrentRow);
};

// 复选事件
const handleRowSelectionChange = (rows: any | undefined) => {
  // rows 为数组
  // console.log(`handleRowSelectionChange `, rows);
  state.multipleSelection = rows;
  if (props.rowSelectCurrentChange != undefined) {
    props.rowSelectCurrentChange(rows);
  }
  emit("on-row-selection-change", rows);
};

// 双击事件
const handleRowDblclick = (row, column) => {
  // console.log("handleRowDblclick", row);
  if (props.rowDblclick != undefined) {
    props.rowDblclick(row, column);
  }
  emit("on-row-dblclick", row, column);
};

// 返回行选的数据
const getCurrentRow = () => {
  return state.currentRow;
};
// 返回复选的数据
const getCheckRows = () => {
  return state.multipleSelection;
};

// 返回当前页的数据
const getRows = () => {
  return state.rows;
};

defineExpose({
  getCurrentRow,
  getCheckRows,
  getRows,
  loadGridData,
});
</script>
<style lang="scss" scoped></style>
